/**
 * @file
 * @author dousha
 * @date 2020/11/30.
 */

#include "saved_secrets.h"
#include <string.h>
#include <stm32f1xx_hal.h>
#include <sm3.h>
#include "convert.h"
#include "crypto_core.h"
#include "../Peripherals/flash.h"
#include "../Peripherals/eeprom.h"

static uint8_t masterSecret[16] = {0};
static uint8_t buf[32];
static volatile secret_index secretIndex;
static uint16_t rwOffset = 0;

uint8_t secret_unlock(uint32_t buttons) {
	set_u32(buf, buttons);
	HAL_GetUID((uint32_t *) (buf + 4));
	flash_read_salt((uint32_t *) (buf + 16));
	sm3_init();
	uint32_t *digest = sm3_digest(buf, 32 * 8);
	memcpy(masterSecret, digest, 16);
	return verify_login((uint32_t *) masterSecret);
}

void secret_generate(uint32_t buttons) {
	set_u32(buf, buttons);
	HAL_GetUID((uint32_t *) (buf + 4));
	generate_salt(buf + 16);
	flash_write_salt((const uint32_t *) (buf + 16));
	sm3_init();
	uint32_t *digest = sm3_digest(buf, 32 * 8);
	memcpy(masterSecret, digest, 16);
	create_credential((uint32_t *) masterSecret);
	secret_init();
}

void secret_init() {
	memset((uint8_t *) secretIndex.name, 0, 16);
	secretIndex.pos = 0;
	secretIndex.length = 0;
	secretIndex.day = 0;
	secretIndex.month = 0;
	secretIndex.year = 0;
	secretIndex.hour = 0;
	secretIndex.minute = 0;
	secretIndex.metadata = 0;
	for (uint8_t i = 0; i < SECRET_INDEX_TABLE_SIZE; i++) {
		eeprom_write_page(EEPROM_SECRET_ADDR + i * 32, (uint8_t *) &secretIndex);
	}
}

uint8_t secret_allocate() {
	uint8_t i = 0;
	uint8_t found = 0;
	uint16_t pos = 0;
	for (; i < SECRET_INDEX_TABLE_SIZE; i++) {
		eeprom_read(EEPROM_SECRET_ADDR + i * 32, (uint8_t *) &secretIndex, 32);
		if (secretIndex.metadata == 0) {
			if (pos == 0) {
				pos = SECRET_INDEX_TABLE_SIZE * 32 + EEPROM_SECRET_ADDR;
			}
			secretIndex.metadata |= SECRET_META_USED;
			found = 1;
			break;
		} else {
			pos = secretIndex.pos + secretIndex.length;
		}
	}
	pos += 31;
	pos &= 0xffe0u;
	secretIndex.pos = pos;
	if (found) {
		return i;
	} else {
		return 0xff;
	}
}

void secret_index_load(uint16_t i) {
	if (i < SECRET_INDEX_TABLE_SIZE) {
		eeprom_read_page(EEPROM_SECRET_ADDR + i * 32, (uint8_t *) &secretIndex);
	}
}

void secret_index_save(uint16_t i) {
	if (i < SECRET_INDEX_TABLE_SIZE) {
		eeprom_write_page(EEPROM_SECRET_ADDR + i * 32, (uint8_t *) &secretIndex);
	}
}

void secret_set_time(uint16_t year, uint16_t month, uint16_t day, uint16_t hour, uint16_t minute) {
	secretIndex.year = year;
	secretIndex.month = month;
	secretIndex.day = day;
	secretIndex.hour = hour;
	secretIndex.minute = minute;
}

void secret_touch(uint8_t index, uint16_t year, uint16_t month, uint16_t day, uint16_t hour, uint16_t minute) {
	secret_index_load(index);
	secret_set_time(year, month, day, hour, minute);
	secret_index_save(index);
}

void secret_get_prepare(uint8_t index) {
	rwOffset = 0;
	secret_index_load(index);
	prepare_decrypt();
}

void secret_set_prepare() {
	rwOffset = 0;
	prepare_encrypt();
}

uint8_t secret_read_page(uint8_t out[32]) {
	eeprom_read_page(secretIndex.pos + rwOffset, buf);
	decrypt_block(buf, out);
	decrypt_block(buf + 16, out + 16);
	rwOffset += 32;
	return rwOffset < secretIndex.length;
}

uint8_t secret_write_page(const uint8_t in[32]) {
	encrypt_block(in, buf);
	encrypt_block(in + 16, buf + 16);
	eeprom_write_page(secretIndex.pos + rwOffset, buf);
	rwOffset += 32;
	return rwOffset < secretIndex.length;
}

uint8_t string_equal(const char *a, const char *b, uint8_t len) {
	uint8_t out = 1;
	for (uint8_t i = 0; i < len; i++) {
		if (a[i] == 0 || b[i] == 0) {
			break;
		}
		out = a[i] == b[i];
	}
	return out;
}

uint8_t secret_seek(const char *name, uint8_t length) {
	if (name == NULL || name[0] == 0) {
		return 0xff;
	}
	for (uint8_t i = 0; i < SECRET_INDEX_TABLE_SIZE; i++) {
		secret_index_load(i);
		if (secretIndex.metadata != 0) {
			if (strncmp(name, (const char *) secretIndex.name, length) == 0) {
				return i;
			}
		}
	}
	return 0xff;
}

void secret_remove(uint16_t offset) {
	// TODO
}

volatile secret_index *secret_current_get() {
	return &secretIndex;
}

uint8_t secret_index_is_valid(uint16_t i) {
	secret_index_load(i);
	return strlen((const char *) secretIndex.name) > 0;
}

const uint8_t *secret_master_secret() {
	return masterSecret;
}
